Imports System.Collections.Generic
Imports System.Text
Imports System.IO
Imports System.Diagnostics


Class angEMLReader
    Private _x_Sender As String
    Public Property X_Sender() As String
        Get
            Return _x_Sender
        End Get
        Set(ByVal value As String)
            _x_Sender = value
        End Set
    End Property

    Private _x_Receivers As String()
    Public Property X_Receivers() As String()
        Get
            Return _x_Receivers
        End Get
        Set(ByVal value As String())
            _x_Receivers = value
        End Set
    End Property

    Private _Received As String
    Public Property Received() As String
        Get
            Return _Received
        End Get
        Set(ByVal value As String)
            _Received = value
        End Set
    End Property

    Private _Mime_Version As String
    Public Property Mime_Version() As String
        Get
            Return _Mime_Version
        End Get
        Set(ByVal value As String)
            _Mime_Version = value
        End Set
    End Property

    Private _From As String
    Public Property From() As String
        Get
            Return _From
        End Get
        Set(ByVal value As String)
            _From = value
        End Set
    End Property

    Private _To As String
    Public Property [To]() As String
        Get
            Return _To
        End Get
        Set(ByVal value As String)
            _To = value
        End Set
    End Property

    Private _CC As String
    Public Property CC() As String
        Get
            Return _CC
        End Get
        Set(ByVal value As String)
            _CC = value
        End Set
    End Property

    Private _Date As DateTime = DateTime.MinValue
    Public Property [Date]() As DateTime
        Get
            Return _Date
        End Get
        Set(ByVal value As DateTime)
            _Date = value
        End Set
    End Property

    Private _Subject As String
    Public Property Subject() As String
        Get
            Return _Subject
        End Get
        Set(ByVal value As String)
            _Subject = value
        End Set
    End Property

    Private _Content_Type As String
    Public Property Content_Type() As String
        Get
            Return _Content_Type
        End Get
        Set(ByVal value As String)
            _Content_Type = value
        End Set
    End Property

    Private _Content_Transfer_Encoding As String
    Public Property Content_Transfer_Encoding() As String
        Get
            Return _Content_Transfer_Encoding
        End Get
        Set(ByVal value As String)
            _Content_Transfer_Encoding = value
        End Set
    End Property

    Private _Return_Path As String
    Public Property Return_Path() As String
        Get
            Return _Return_Path
        End Get
        Set(ByVal value As String)
            _Return_Path = value
        End Set
    End Property

    Private _Message_ID As String
    Public Property Message_ID() As String
        Get
            Return _Message_ID
        End Get
        Set(ByVal value As String)
            _Message_ID = value
        End Set
    End Property

    Private _x_OriginalArrivalTime As DateTime = DateTime.MinValue
    Public Property X_OriginalArrivalTime() As DateTime
        Get
            Return _x_OriginalArrivalTime
        End Get
        Set(ByVal value As DateTime)
            _x_OriginalArrivalTime = value
        End Set
    End Property

    Private _Body As String
    Public Property Body() As String
        Get
            Return _Body
        End Get
        Set(ByVal value As String)
            _Body = value
        End Set
    End Property

    Private _HTMLBody As String
    Public Property HTMLBody() As String
        Get
            Return _HTMLBody
        End Get
        Set(ByVal value As String)
            _HTMLBody = value
        End Set
    End Property

    Private _listUnsupported As Dictionary(Of String, String) = Nothing
    Public ReadOnly Property UnsupportedHeaders() As Dictionary(Of String, String)
        Get
            Return _listUnsupported
        End Get
    End Property

    Public Sub New(ByVal fsEML As FileStream)
        ParseEML(fsEML)
    End Sub

    Private Sub ParseEML(ByVal fsEML As FileStream)
        Dim sr As New StreamReader(fsEML)
        Dim sLine As String = ""
        Dim listAll As New List(Of String)()
        While (InlineAssignHelper(sLine, sr.ReadLine())) IsNot Nothing
            listAll.Add(sLine)
        End While

        Dim list As New List(Of String)()
        Dim nStartBody As Integer = -1
        Dim saAll As String() = New String(listAll.Count - 1) {}
        listAll.CopyTo(saAll)

        For i As Integer = 0 To saAll.Length - 1
            If saAll(i) = String.Empty Then
                nStartBody = i
                Exit For
            End If

            Dim sFullValue As String = saAll(i)
            GetFullValue(saAll, i, sFullValue)
            list.Add(sFullValue)


            Debug.WriteLine(sFullValue)
        Next

        SetFields(list.ToArray())

        If nStartBody = -1 Then
            ' no body ?
            Return
        End If

        ' Get the body info out of saAll and set the Body and/or HTMLBody properties
        If Content_Type IsNot Nothing AndAlso Content_Type.ToLower().Contains("multipart/alternative") Then

            ' set for HTMLBody messages
            Dim ix As Integer = Content_Type.ToLower().IndexOf("boundary")
            ' boundary is used to separate the different body types
            If ix = -1 Then
                Return
            End If

            Dim sBoundaryMarker As String = Content_Type.Substring(ix + 8).Trim(New Char() {"="c, """"c, " "c, ControlChars.Tab})

            ' save this boundaries elements into a list of strings
            list = New List(Of String)()
            For n As Integer = nStartBody + 1 To saAll.Length - 1
                If saAll(n).Contains(sBoundaryMarker) Then
                    If list.Count > 0 Then
                        SetBody(list)
                        list = New List(Of String)()
                    End If
                    Continue For
                End If

                list.Add(saAll(n))
            Next
        Else
            ' plain text body type only
            Body = String.Empty
            For n As Integer = nStartBody + 1 To saAll.Length - 1
                Body += saAll(n) & vbCr & vbLf
            Next
        End If
    End Sub

    Private Sub SetBody(ByVal list As List(Of String))
        Dim bIsHTML As Boolean = False
        Dim bIsBodyStart As Boolean = False
        Dim listBody As New List(Of String)()

        For Each s As String In list
            ' use to determine type of body
            If s.ToLower().StartsWith("content-type") Then
                If s.ToLower().Contains("text/html") Then
                    bIsHTML = True
                ElseIf Not s.ToLower().Contains("text/plain") Then
                    Return
                End If
            ElseIf s = String.Empty AndAlso Not bIsBodyStart Then
                bIsBodyStart = True
            ElseIf bIsBodyStart Then
                listBody.Add(s)
            End If
        Next

        Dim sa As String() = New String(listBody.Count - 1) {}
        listBody.CopyTo(sa)

        If bIsHTML Then
            HTMLBody = String.Join(vbCr & vbLf, sa)
        Else
            Body = String.Join(vbCr & vbLf, sa)
        End If
    End Sub

    Private Sub GetFullValue(ByVal sa As String(), ByRef i As Integer, ByRef sValue As String)
        If i + 1 < sa.Length AndAlso sa(i + 1) <> String.Empty AndAlso Char.IsWhiteSpace(sa(i + 1), 0) Then
            ' spec says line's that begin with white space are continuation lines
            i += 1
            sValue += " " & sa(i).Trim()

            GetFullValue(sa, i, sValue)
        End If
    End Sub

    Private Sub SetFields(ByVal saLines As String())
        'List<string> listUnsupported = new List<string>();
        _listUnsupported = New Dictionary(Of String, String)()
        Dim listX_Receiver As New List(Of String)()
        For Each sHdr As String In saLines
            Dim saHdr As String() = Split(sHdr)
            If saHdr Is Nothing Then
                ' not a valid header
                Continue For
            End If

            Select Case saHdr(0).ToLower()
                Case "x-sender"
                    X_Sender = saHdr(1)
                    Exit Select
                Case "x-receiver"
                    listX_Receiver.Add(saHdr(1))
                    Exit Select
                Case "received"
                    Received = saHdr(1)
                    Exit Select
                Case "mime-version"
                    Mime_Version = saHdr(1)
                    Exit Select
                Case "from"
                    From = saHdr(1)
                    Exit Select
                Case "to"
                    [To] = saHdr(1)
                    Exit Select
                Case "cc"
                    CC = saHdr(1)
                    Exit Select
                Case "date"
                    [Date] = DateTime.Parse(saHdr(1))
                    Exit Select
                Case "subject"
                    Subject = saHdr(1)
                    Exit Select
                Case "content-type"
                    Content_Type = saHdr(1)
                    Exit Select
                Case "content-transfer-encoding"
                    Content_Transfer_Encoding = saHdr(1)
                    Exit Select
                Case "return-path"
                    Return_Path = saHdr(1)
                    Exit Select
                Case "message-id"
                    Message_ID = saHdr(1)
                    Exit Select
                Case "x-originalarrivaltime"
                    Dim ix As Integer = saHdr(1).IndexOf("FILETIME")
                    If ix <> -1 Then
                        Dim sOAT As String = saHdr(1).Substring(0, ix)
                        sOAT = sOAT.Replace("(UTC)", "-0000")
                        X_OriginalArrivalTime = DateTime.Parse(sOAT)
                    End If
                    Exit Select
                Case Else
                    'case "body":
                    '    Body = saHdr[1];
                    '    break;
                    _listUnsupported.Add(saHdr(0), saHdr(1))
                    Exit Select
            End Select
        Next

        X_Receivers = New String(listX_Receiver.Count - 1) {}
        listX_Receiver.CopyTo(X_Receivers)
    End Sub

    Private Function Split(ByVal sHeader As String) As String()
        ' because string.Split won't work here...
        Dim ix As Integer
        If (InlineAssignHelper(ix, sHeader.IndexOf(":"c))) = -1 Then
            Return Nothing
        End If

        Return New String() {sHeader.Substring(0, ix).Trim(), sHeader.Substring(ix + 1).Trim()}
    End Function
    Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
        target = value
        Return value
    End Function
End Class


